﻿#include <algorithm>
#include <fstream>
#include <gtest/gtest.h>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <vector>

#include <learn/utils/generator.h>
#include <learn/utils/json.h>
#include <learn/utils/reflection.h>
#include <learn/utils/serialization.h>

using namespace xi;
using namespace xi::utils;

class A : public Serializable
{
  public:
    // 使用宏定义序列化
    SERIALIZE(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)

  public:
    bool a{};
    char b{};
    int32_t c{};
    int64_t d{};
    float e{};
    float f{};
    double g{};
    std::string h{};
    std::vector<int> i{};
    std::vector<std::string> j{};
    std::vector<std::vector<int>> k{};
    std::vector<std::vector<std::string>> l{};
    std::list<int> m{};
    std::list<std::string> n{};
    std::list<std::vector<int>> o{};
    std::map<int, std::string> p{};
    std::map<std::string, int> q{};
    std::set<int> r{};
    std::set<std::string> s{};
};

class B : public ReflectionObject
{
  public:
    B() : m_name("A"), m_age(10)
    {
    }

    void f(int age)
    {
        m_age = age;
        std::cout << "B::f(" << age << ")" << std::endl;
    }

    void g(std::string s)
    {
        m_name = s;
        std::cout << "B::g(" << s << ")" << std::endl;
    }

    int h() const
    {
        std::cout << "B::h()" << std::endl;
        return m_age;
    }

  public:
    std::string m_name;
    int m_age;
};

REGISTER_CLASS(B)
REGISTER_FIELD(B, m_name, std::string)
REGISTER_FIELD(B, m_age, int)
REGISTER_METHOD(B, f, void, int)
REGISTER_METHOD(B, g, void, std::string)
REGISTER_METHOD(B, h, int)

TEST(TestUtils, Serializable)
{
    A a;
    a.a = true;
    a.b = 'b';
    a.c = 123;
    a.d = (int64_t)123456;
    a.e = 1.23;
    a.f = 4.56f;
    a.g = 7.89f;
    a.h = "hello world";
    a.i = {1, 2, 3};
    a.j = {"hello", "world"};
    a.k = {{1, 2, 3}, {4, 5, 6}};
    a.l = {{"hello", "world"}, {"xi", "lang"}};
    a.m = {1, 2, 3};
    a.n = {"hello", "world"};
    a.o = {{1, 2, 3}, {4, 5, 6}};
    a.p = {{1, "hello"}, {2, "world"}};
    a.q = {{"hello", 1}, {"world", 2}};
    a.r = {1, 2, 3};
    a.s = {"hello", "world"};
    auto ss = a.serialize();

    A b;
    b.deserialize(ss);
    EXPECT_EQ(a.a, b.a);
    EXPECT_EQ(a.b, b.b);
    EXPECT_EQ(a.c, b.c);
    EXPECT_EQ(a.d, b.d);
    EXPECT_EQ(a.e, b.e);
    EXPECT_EQ(a.f, b.f);
    EXPECT_EQ(a.g, b.g);
    EXPECT_EQ(a.h, b.h);
    EXPECT_EQ(a.i, b.i);
    EXPECT_EQ(a.j, b.j);
    EXPECT_EQ(a.k, b.k);
    EXPECT_EQ(a.l, b.l);
    EXPECT_EQ(a.m, b.m);
    EXPECT_EQ(a.n, b.n);
    EXPECT_EQ(a.o, b.o);
    EXPECT_EQ(a.p, b.p);
    EXPECT_EQ(a.q, b.q);
    EXPECT_EQ(a.r, b.r);
    EXPECT_EQ(a.s, b.s);
}

TEST(TestUtils, Json)
{
    // std::cout << std::is_convertible_v<std::monostate, std::variant<std::monostate, bool, int, double, std::string>>
    // << std::endl; std::cout << std::is_convertible_v<bool, std::variant<std::monostate, bool, int, double,
    // std::string>> << std::endl; std::cout << std::is_convertible_v<int, std::variant<std::monostate, bool, int,
    // double, std::string>> << std::endl; std::cout << std::is_convertible_v<double, std::variant<std::monostate, bool,
    // int, double, std::string>> << std::endl; std::cout << std::is_convertible_v<std::string,
    // std::variant<std::monostate, bool, int, double, std::string>> << std::endl;

    // 初始化
    {
        Json j1;
        std::cout << j1.dump() << std::endl;

        Json j2(true);
        std::cout << j2.dump() << std::endl;
        std::cout << j2.as<bool>() << std::endl;

        Json j3(false);
        std::cout << j3.dump() << std::endl;
        std::cout << j3.as<bool>() << std::endl;

        Json j4(123);
        std::cout << j4.dump() << std::endl;
        std::cout << j4.as<int>() << std::endl;

        Json j5(1.27f);
        std::cout << j5.dump() << std::endl;
        std::cout << j5.as<double>() << std::endl;

        Json j6(std::string("hello"));
        std::cout << j6.dump() << std::endl;
        std::cout << j6.as<std::string>() << std::endl;

        Json j7("world");
        std::cout << j7.dump() << std::endl;
        std::cout << j7.as<std::string>() << std::endl;

        Json j8;
        j8["key"] = "value";
        j8["key2"] = std::move(j2);
        j8["key3"] = std::move(j3);
        j8["key4"] = std::move(j4);
        j8.remove("key3");
        std::cout << j8.dump() << std::endl;
        std::cout << j2.dump() << std::endl;

        EXPECT_TRUE(j8.contains("key"));
        EXPECT_TRUE(j8.contains("key2"));
        EXPECT_FALSE(j8.contains("key3"));
        EXPECT_TRUE(j8.contains("key4"));

        Json j9;
        j9[1] = "value";
        j9[2] = std::move(j8);
        j9.append(std::move(j5));
        j9.remove(1);
        std::cout << j9.dump() << std::endl;

        for (auto it = j9.begin(); it != j9.end(); ++it)
            std::cout << it->dump() << std::endl;

        EXPECT_TRUE(j9.contains(0));
        EXPECT_TRUE(j9.contains(1));
        EXPECT_TRUE(j9.contains(2));
        EXPECT_FALSE(j9.contains(3));
    }

    // 解析
    {
        std::fstream file(TEST_DATA_PATH "/test.json", std::ios::in);
        if (!file.is_open())
        {
            std::cerr << "Failed to open file: " << TEST_DATA_PATH "test.json" << std::endl;
            return;
        }
        std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        file.close();

        Json j = parse(std::move(content));

        std::fstream out(TEST_DATA_PATH "/test_out.json", std::ios::out);
        out << j.dump() << std::endl;
        out.close();
    }
}

TEST(TestUtils, Reflection)
{
    auto &factory = ReflectionFactory::instance();
    auto a = factory.create_class("B");

    a->set_value("m_name", std::string("Alice"));
    a->set_value("m_age", 20);

    std::string name;
    int age;

    a->value("m_name", name);
    a->value("m_age", age);

    std::cout << "Name: " << name << std::endl;
    std::cout << "Age: " << age << std::endl;

    int count = a->field_num();
    for (int i = 0; i < count; i++)
    {
        std::string fieldName = a->field(i)->name();
        std::string fieldType = a->field(i)->type();
        size_t offset = a->field(i)->offset();
        std::cout << fieldName << ", " << fieldType << ", " << offset << std::endl;
    }

    int methodCount = a->method_num();
    for (int i = 0; i < methodCount; i++)
    {
        std::string methodName = a->method(i)->name();
        std::cout << methodName << std::endl;
    }

    a->call("f", 10);
    a->call("g", std::string("Bob"));
    a->call<int>("h");
}

using promise = Generator<int>;

promise iota(int value)
{
    std::cout << "iota(" << value << ")" << std::endl;
    while (value > 5)
    {
        co_yield value--;
    }
    // co_return;
}

TEST(TestUtils, co)
{
    for (int x : iota(10))
        std::cout << x << " ";
    std::cout << std::endl;
}